home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume90
/
util
/
tracktls
/
part01
/
src
/
tcopy.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-01-20
|
23KB
|
812 lines
/*
* TRACKCOPY.C
*
* (C) Copyright Eddy Carroll 1989
*
* This program allows you to copy a portion of one disk device to a portion
* of another device which has similar sector and track layout, though may
* have a different number of cylinders. It is primarily intended to allow
* a recoverable ram disk like RAD: to be preloaded directly from floppy
* disk at high speed, a track at a time, instead of having to copy in files
* manually.
*
* Usage: tcopy <srcdevice> <destdevice> <start> <stop> <newstart>
*
* The parameters are as follows (example values are in brackets)
*
* <srcdevice> The device to copy from (DF0:)
* <destdevice> The device to copy to (RAD:)
* <start> The starting cylinder on <srcdevice> (60)
* <end> The ending cylinder on <srcdevice> (79)
* <newstart> The starting cylinder on <destdevice> (0)
*
* For example, supposing you want to setup a disk that you can load into
* a 264K RAD (equals 24 cylinders of 2 tracks of 11 sectors of 512 bytes).
* First of all, you setup RAD: manually by mounting it and then copying
* in the appropriate files. Then you put a blank formatted disk into
* drive 0 and give the command:
*
* tcopy RAD: DF0: 0 23 56
*
* Meaning: Copy cylinders 0-23 of RAD: to cylinders 56-79 of DF0:.
*
* You then add enough files to DF0: to make a minimal boot disk, taking care
* not to add more than (56-40 = 16) cylinders of data (or 176K), since
* this would cause the data you've just written directly onto the disk
* to be overwritten. Then put the command:
*
* tcopy DF0: RAD: 56 79 0
*
* into your startup-sequence, and when you reboot, RAD: will be reloaded
* with the appropriate information.
* ----
* An additional option may be compiled in by defining the constant
* RAMBO at compile time. When this is done, tcopy will have
* some additional options which allow rad: to be automatically mounted
* and initialised. This is designed to remove the overhead of having
* to do a MOUNT RAD: in your startup-sequence. When this is the case,
* the following options may be specified before the rest of the command
* line. If none are specified, RAD: is not mounted. Defaults are in ().
*
* -v<name> Sets RAD: volume name to be <name> ("RAMB0")
* -n<name> Sets RAD: device name to be <name> ("RAD") (no colon!)
* -d<name> Sets RAD: exec device to be <name> ("ramdrive.device")
* -e<txt> Echos <txt> to stdout
* -a Always do track copy, even if RAD: already mounted
* -f# Sets flags for OpenDevice() to # (0)
* -m# Sets BufMemType to # (1)
* -p# Sets boot priority to # (-1)
* -t# Sets number of tracks(%) to # (10)
* -u# Sets unit number of device to be # (0)
*
* (%) Actually the number of cylinders, but people seem to like to think
* of them as tracks.
*/
#define TRUE 1
#define FALSE 0
#define LASTCHAR(s) (s[strlen(s)-1])
#ifndef LATTICE_50
#include "system.h"
#endif
extern struct DosLibrary *DOSBase;
typedef struct IORequest IOREQ;
typedef struct MsgPort MSGPORT;
typedef struct Process PROC;
typedef struct StandardPacket STDPKT;
void inhibit(MSGPORT *devport, int mode);
/*
* Structure representing a disk device
*/
typedef struct {
char *devname; /* Name of exec device for this disk */
int isfloppy; /* True if device is trackdisk.device */
ULONG unit; /* Unit number of above exec device */
ULONG blocksize; /* Number of bytes per block */
ULONG blockspertrack; /* Number of blocks/sectors per track */
ULONG surfaces; /* Number of tracks per cylinder */
ULONG lowcyl; /* Starting cylinder of disk on device */
ULONG numcyl; /* Number of cylinders on this disk */
} DISKDEV;
#ifdef RAMBO
/************************** Declarations for RAMBO **************************/
struct ExpansionBase *ExpansionBase;
char execname[] = "ramdrive.device";
char dosname[] = "RAD";
char *volname = "RAMB0";
int bootpri = -1;
/*
* Parameter block for mounting RAD:
*/
struct MountParamBlock {
char *dosname;
char *execname;
ULONG unit;
ULONG flags;
ULONG size;
ULONG blocksize;
ULONG origin;
ULONG surfaces;
ULONG sectorperblock;
ULONG sectorspertrack;
ULONG reserved;
ULONG prealloc;
ULONG interleave;
ULONG lowcyl;
ULONG highcyl;
ULONG numbuffers;
ULONG bufmemtype;
} mount = {
dosname,
execname,
0, /* 2: Unit number */
0, /* 3: OpenDevice() flags */
/* This is where the environment block starts */
11, /* 4: Table upper bound */
512>>2, /* 5: Number of longwords per block */
0, /* 6: Sector origin - unused */
2, /* 7: Number of surfaces */
1, /* 8: Sectors per block - unused */
11, /* 9: Sectors per track */
2, /* 10: Reserved blocks - boot block */
0, /* 11: Reserved blocks at end */
0, /* 12: Interleave */
0, /* 13: Low cylinder */
10, /* 14: High cylinder */
5 /* 15: Number of buffers */
};
/*********************** End of declarations for RAMBO *********************/
#endif RAMBO
/*
* Global variables
*/
char *srcname; /* AmigaDos name of source device */
char *destname; /* AmigaDos name of destination device */
DISKDEV src[1]; /* Source disk device */
DISKDEV dest[1]; /* Destination disk device */
struct IOStdReq *srcreq; /* Standard request for source device */
struct IOStdReq *destreq; /* Standard request for destination device */
MSGPORT *reqport; /* Message port for replies from devices */
MSGPORT *destport; /* Message port of process of dest. device */
void *buffer; /* Pointer to data buffer for track r/w */
long cylsize; /* Size in bytes of a single cylinder */
int srcdevopen; /* True if source exec device is open */
int destdevopen; /* True if destination device is open */
int inhibited; /* True if destination device is inhibited */
/*
* print()
* -------
* Outputs a message to stdout
*/
void print(char *s)
{
Write(Output(), s, strlen(s));
}
#define print2(s1,s2) (print(s1),print(s2))
#define print3(s1,s2,s3) (print(s1),print(s2),print(s3))
/*
* numtostr()
* ----------
* Simple little function which returns a pointer to a static string
* representation of the passed in number.
*/
char *numtostr(int n)
{
static char s[20];
int i = 19;
s[19] = '\0';
if (n)
while (n) {
s[--i] = '0' + (n % 10);
n /= 10;
}
else
s[--i] = '0';
return(&s[i]);
}
/*
* cleanup()
* ---------
* Closes all opened resources, and exits with specified error code.
*/
void cleanup(int code)
{
if (buffer)
FreeMem(buffer, cylsize);
if (srcdevopen) {
if (src->isfloppy) { /* Turn off drive motor if floppy disk */
srcreq->io_Command = TD_MOTOR;
srcreq->io_Length = 0;
DoIO((IOREQ *)srcreq);
}
CloseDevice((IOREQ *)srcreq);
}
if (destdevopen) {
if (dest->isfloppy) { /* Turn off drive motor if floppy disk */
destreq->io_Command = TD_MOTOR;
destreq->io_Length = 0;
DoIO((IOREQ *)destreq);
}
CloseDevice((IOREQ *)destreq);
}
if (inhibited)
inhibit(destport, FALSE);
if (srcreq)
DeleteStdIO(srcreq);
if (destreq)
DeleteStdIO(destreq);
if (reqport)
DeletePort(reqport);
#ifdef RAMBO
if (ExpansionBase)
CloseLibrary((struct Library *)ExpansionBase);
#endif RAMBO
exit(code);
}
/*
* chkabort()
* ----------
* A replacement for Lattice's chkabort(), which doesn't carry all
* the extra baggage. If CTRL-C is detected, this function never
* returns but instead calls cleanup. Since Lattice's exit() code
* may call chkabort(), a check is made to ensure that cleanup()
* only gets called once, otherwise there would be a problem if the
* user pressed Ctrl-C twice in quick succession.
*/
void chkabort()
{
static int gotctrlc = FALSE;
if (!gotctrlc && (SetSignal(0,0) & SIGBREAKF_CTRL_C)) {
gotctrlc = TRUE;
print("^C\n");
cleanup(20);
}
}
/*
* GetVolume()
* -----------
* This function searches the device list for the named volume, and
* fills in the passed DISKDEV structure with information about the
* volume. If the named volume is not a device, or is not a disk,
* then FALSE is returned. It is not an error to pass in NULL as
* a pointer to the DISKDEV structure. In this case, nothing will
* be filled in, but TRUE or FALSE will be returned indicating whether
* the device is a disk device or not.
*/
int GetVolume(char *devname, DISKDEV *dev)
{
struct RootNode *rootnode;
struct DosInfo *dosinfo;
struct DeviceNode *devnode;
struct FileSysStartupMsg *startup;
struct DosEnvec *dosenvec;
unsigned char *p;
int namelen = strlen(devname);
if (LASTCHAR(devname) != ':') /* Device names must end with ':' */
return (FALSE);
/*
* First of all, find the device
*/
rootnode = (struct RootNode *)DOSBase->dl_Root;
dosinfo = (struct DosInfo *)BADDR(rootnode->rn_Info);
devnode = (struct DeviceNode *)BADDR(dosinfo->di_DevInfo);
Forbid(); /* Make sure list doesn't change while we scan it */
while (devnode != NULL) {
p = (unsigned char *)BADDR(devnode->dn_Name)+1;
if (!strnicmp(devname, p, namelen-1)) { /* Don't compare the ':' */
/*
* Matched name successfully. Now check if it's a device.
* Note that we carry on searching if it's not a device
* (rather than returning FALSE immediately) since there
* may be a volume called RAD: as well as a device called
* RAD:, for example.
*/
if (devnode->dn_Type == DLT_DEVICE) {
if (devnode->dn_Startup < 20) /* Is there a startup bit? */
goto notfound; /* If not, not disk device */
/* Eek! A GOTO! */
startup = (struct FileSysStartupMsg *)
BADDR(devnode->dn_Startup);
if (dev) {
dev->devname = ((char *)BADDR(startup->fssm_Device))+1;
dev->isfloppy = (!strcmp(TD_NAME, dev->devname));
dev->unit = startup->fssm_Unit;
}
if (startup->fssm_Environ < 20)
goto notfound;
/* Another GOTO! The Earth will end in 5 seconds... */
dosenvec = (struct DosEnvec *)BADDR(startup->fssm_Environ);
if (dev) {
dev->blocksize = dosenvec->de_SizeBlock << 2;
dev->blockspertrack = dosenvec->de_BlocksPerTrack;
dev->surfaces = dosenvec->de_Surfaces;
dev->lowcyl = dosenvec->de_LowCyl;
dev->numcyl = (dosenvec->de_HighCyl -
dosenvec->de_LowCyl) + 1;
}
Permit();
return (TRUE);
}
}
devnode = (struct DeviceNode *)BADDR(devnode->dn_Next);
}
notfound:
Permit();
return (FALSE);
}
/*
* help()
* ------
* Prints out a help message about tcopy
*/
void help()
{
print(
"Tcopy (C) Copyright Eddy Carroll, January 1990. Freely distributable.\n"
);
#ifdef RAMBO
#define print13(a,b,c,d,e,f,g,h,i,j,k,l,m) (print3(a,b,c),print3(d,e,f),\
print3(g,h,i),print3(j,k,l),print(m))
print13(
"Usage: tcopy <flags> <from> <to> <start> <end> <newstart>\n\n",
" <flags> If any of the following are included, then RAD: will be\n",
" automatically mounted when tcopy is run:\n\n",
" -v<name> Sets RAD: volume name to be <name> (\"RAMB0\")\n",
" -n<name> Sets RAD: device name to be <name> (\"RAD\")\n",
" -d<name> Use exec device <name> (\"ramdrive.device\")\n",
" \"-e<txt>\" Prints <txt> to stdout\n",
" -a Do trackcopy, even if RAD: already mounted\n",
" -f# Sets flags for OpenDevice() to # (0)\n",
" -m# Sets BufMemType to # (1)\n",
" -p# Sets boot priority to # (-1)\n",
" -t# Sets number of tracks to # (10)\n",
" -u# Sets unit number of exec device to # (0)\n\n"
);
#else
print("Usage: tcopy <from> <to> <start> <end> <newstart>\n\n");
#endif RAMBO
print(" <from> Device to copy from (e.g. DF0:)\n");
print(" <to> Device to copy to (e.g. RAD:)\n");
print(" <start> Cylinder to start reading from (e.g. 70)\n");
print(" <end> Cylinder to end reading at (e.g. 79)\n");
print(" <newstart> Cylinder to start writing at (e.g. 0)\n");
}
/*
* opendevs()
* ----------
* Opens the source and destination devices, allocates buffer for
* reading and writing etc. Note that if either device is a floppy
* drive, the buffer must be allocated in chip memory or trackdisk.device
* won't be able to blit into it.
*/
void opendevs()
{
long memflags = 0;
if (src->isfloppy || dest->isfloppy)
memflags |= MEMF_CHIP;
cylsize = src->blocksize * src->blockspertrack * src->surfaces;
buffer = AllocMem(cylsize, memflags);
if (!buffer) {
print("Not enough memory for track buffer\n");
cleanup(20);
}
reqport = (MSGPORT *)CreatePort(0,0);
if (!reqport) {
print("Couldn't allocate message port\n");
cleanup(20);
}
srcreq = CreateStdIO(reqport);
destreq = CreateStdIO(reqport);
if (!srcreq || !destreq) {
print("Couldn't allocate IO request - memory is low!\n");
cleanup(20);
}
if (OpenDevice(src->devname, src->unit, (IOREQ *)srcreq, 0L)) {
print3("Can't open source ", src->devname, "\n");
cleanup(20);
}
srcdevopen = TRUE;
if (OpenDevice(dest->devname, dest->unit, (IOREQ *)destreq, 0L)) {
print3("Can't open destination ", dest->devname, "\n");
cleanup(20);
}
destdevopen = TRUE;
}
/*
* copytracks()
* ------------
* This is where the actual work gets done. Tracks (cylinders actually)
* are copied from start to end on the source device to newstart on
* the destination device.
*/
void copytracks(int start, int end, int newstart)
{
int cyl, retry, numcyls = (end - start) + 1;
for (cyl = 0; cyl < numcyls; cyl++) {
/*
* First read in track from source device
*/
for (retry = 0; retry < 3; retry++) {
chkabort();
srcreq->io_Command = CMD_READ;
srcreq->io_Length = cylsize;
srcreq->io_Offset = (src->lowcyl + cyl + start) * cylsize;
srcreq->io_Data = buffer;
if (!DoIO((IOREQ *)srcreq))
break; /* Succeeded, so break out of loop */
}
if (retry == 3) {
print3("Error reading track ", numtostr(cyl+start)," from disk\n");
cleanup(20);
}
/*
* Now write out track to destination device
*/
for (retry = 0; retry < 3; retry++) {
chkabort();
destreq->io_Command = CMD_WRITE;
destreq->io_Length = cylsize;
destreq->io_Offset = (dest->lowcyl + cyl + newstart) * cylsize;
destreq->io_Data = buffer;
if (!DoIO((IOREQ *)destreq))
break; /* Succeeded */
}
if (retry == 3) {
print3("Error writing track ", numtostr(cyl), " to disk\n");
cleanup(20);
}
}
}
/*
* SendPacket()
* ------------
* ``Sort of'' simulates the ARP SendPacket() routine which sends
* a packet to AmigaDos, and gets a reply if appropriate. What is
* passed in is the action to be executed (one of the ACTION_xxx
* definitions in dosextens.h), a pointer to a longword array of 7
* arguments to be passed to the device, and the msgport of the device
* as returned by DeviceProc("DF0:") for example. If result is non-NULL
* then it should be a pointer to a two element array of ULONGs, and it
* fills in the 0th element with the primary result, and the the
* 1st element with the secondary result.
*/
int SendPacket(ULONG action, void *args, MSGPORT *devport, ULONG *result)
{
PROC *proc = (PROC *)FindTask(NULL);
STDPKT *packet;
packet = (STDPKT *)AllocMem(sizeof(STDPKT), MEMF_CLEAR | MEMF_PUBLIC);
if (!packet)
return (FALSE);
packet->sp_Msg.mn_Node.ln_Name = (char *)&packet->sp_Pkt;
packet->sp_Pkt.dp_Link = &packet->sp_Msg;
packet->sp_Pkt.dp_Port = &proc->pr_MsgPort;
packet->sp_Pkt.dp_Type = action;
memcpy(&packet->sp_Pkt.dp_Arg1, args, sizeof(ULONG) * 7);
/*
* Okay, we've done the necessary magic to create an AmigaDos
* packet lookalike (thanks to Matt Dillon in Transactor V1.1).
* Now we send the message to the Dos process, and get the reply.
* Then our message will be filled in with the response from the
* Dos process.
*/
PutMsg(devport, (struct Message *)packet);
WaitPort(&proc->pr_MsgPort);
GetMsg(&proc->pr_MsgPort);
if (result) {
result[0] = packet->sp_Pkt.dp_Res1;
result[1] = packet->sp_Pkt.dp_Res2;
}
FreeMem(packet, sizeof(STDPKT));
return (TRUE);
}
/*
* inhibit()
* ---------
* This function inhibits (if mode is TRUE) or hibits (if mode is FALSE)
* (is hibit the opposite of inhibit? Hmmm...) the specified device.
*/
void inhibit(MSGPORT *devport, int mode)
{
ULONG pktargs[7];
int i;
pktargs[0] = mode; /* Select inhibit or opposite */
for (i = 1; i < 7; i++) /* Clear other arguments */
pktargs[i] = 0;
if (!SendPacket(ACTION_INHIBIT, pktargs, devport, NULL)) {
print("Couldn't send inhibit packet to device\n");
cleanup(20);
}
}
#ifdef RAMBO
/*
* mountramdisk()
* --------------
* This procedure simply mounts the device specified in the mount
* parameter block.
*/
int mountramdisk()
{
struct DeviceNode *devnode;
long args[7];
int i;
char *diskname;
MSGPORT *devport;
static char dosname[200]; /* Temporary storage for dos device name */
ExpansionBase = (struct ExpansionBase *)
OpenLibrary("expansion.library", 0L);
if (!ExpansionBase) {
print("Couldn't open expansion.library\n");
cleanup(20);
}
devnode = MakeDosNode(&mount);
if (!devnode)
return (FALSE);
AddDosNode(bootpri, ADNF_STARTPROC, devnode);
/*
* Now we've mounted the device, let's try and rename it to
* something different.
*/
strcpy(dosname, mount.dosname);
strcat(dosname, ":");
devport = (MSGPORT *)DeviceProc(dosname);
if (!devport)
return (FALSE);
for (i = 1; i < 7; i++)
args[i] = 0;
/*
* Some horrible messing around to make a BSTR
*/
diskname = AllocMem(strlen(volname)+1,MEMF_PUBLIC);
strcpy(diskname+1, volname);
*diskname = strlen(volname);
args[0] = ((long)diskname)>>2;
if (!SendPacket(ACTION_RENAME_DISK, args, devport, NULL))
print("(Couldn't relabel disk\n");
/* Don't return an error, even if SendPacket failed! */
FreeMem(diskname, strlen(volname)+1);
return (TRUE);
}
#endif RAMBO
/*
* mainline()
* ----------
*/
void main(argc,argv)
int argc;
char **argv;
{
unsigned int start, end, newstart; /* Cylinder numbers */
#ifdef RAMBO
/************************* Start of RAMBO Stuff ***************************/
int doload = FALSE; /* If true, always copy to device even if mounted */
if (argc > 1 && argv[1][0] == '-') { /* Handle Rambo options */
static char tempname[200];
while (argc > 1 && argv[1][0] == '-') {
char *str = &argv[1][2]; /* Have handy ptr to string */
int num = atoi(str); /* Have parameter ready */
switch (argv[1][1]) {
case 'v': /* Set volume name */
volname = str;
break;
case 'n': /* Set device name */
/* Strip trailing ':' */
if (LASTCHAR(str) == ':')
LASTCHAR(str) = '\0';
mount.dosname = str;
break;
case 'd': /* Set exec device name */
mount.execname = str;
break;
case 'u': /* Set device unit number */
mount.unit = num;
break;
case 'f': /* Set flags for OpenDevice() */
mount.flags = num;
break;
case 't': /* Set number of tracks/cylinders */
mount.highcyl = num - 1;
break;
case 'm': /* Set memory type */
mount.bufmemtype = num;
break;
case 'p': /* Set boot priority */
bootpri = num;
break;
case 'e': /* Echo message to stdout */
print2(&argv[1][2], "\n");
break;
case 'a': /* Force loading into RAD: */
doload = TRUE;
break;
default:
print3("Unknown switch ", argv[1], "\n");
help();
cleanup(5);
}
argv++;
argc--;
}
/*
* Setup defaults for mount okay. Now see if the device is
* to be mounted. If it is, then try and mount it. If it was
* already mounted, then quit immediately, unless forceload is
* in effect.
*
* Else, see are there any files in RAD:. If there are, quit
* immediately (since we are under Kikstart 1.2, and RAD: has
* just recovered itself from memory), unless forceload is
* in effect.
*/
strcpy(tempname, mount.dosname);
strcat(tempname, ":");
if (!GetVolume(tempname, NULL)) {
if (!mountramdisk()) {
print3("Error: Couldn't mount device ",tempname,"\n");
cleanup(20);
}
}
/*
* Scan device and see if there are files on it.
* Do this by seeing if there are any files in
* the directory. If there aren't, then we can
* force the load, otherwise don't force it.
*/
{
struct FileInfoBlock *fib;
BPTR lock;
fib = AllocMem(sizeof(struct FileInfoBlock), 0L);
if (!fib) {
print("Out of memory allocating FileInfoBlock!\n");
cleanup(20);
}
lock = Lock(tempname, ACCESS_READ);
if (lock) {
if (Examine(lock, fib)) {
if (!ExNext(lock, fib))
doload = TRUE;
}
UnLock(lock);
}
FreeMem(fib, sizeof(struct FileInfoBlock));
}
/*
* Now see if after all that, we still want to go
* ahead with the load into RAD:. If not, then just
* exit silently (to let the startup-sequence carry
* on okay).
*/
if (!doload)
cleanup(0);
}
/************************* End of RAMBO Stuff ***************************/
#endif RAMBO
#define NONUM(x) (!isdigit(argv[x][0]))
if (argc != 6 || NONUM(3) || NONUM(4) || NONUM(5)) {
help();
cleanup(5);
}
srcname = argv[1];
destname = argv[2];
start = atoi(argv[3]);
end = atoi(argv[4]);
newstart = atoi(argv[5]);
if (!GetVolume(srcname, src)) {
print2(srcname, " is not a valid disk device\n");
cleanup(20);
}
if (!GetVolume(destname, dest)) {
print2(destname, " is not a valid disk device\n");
cleanup(20);
}
#define NOTSAME(x) (src->x != dest->x)
if (NOTSAME(blocksize) || NOTSAME(blockspertrack) || NOTSAME(surfaces)) {
print3(srcname, " and ", destname);
print(" do not have same track sizes\n");
cleanup(20);
}
if (start > end) {
print("Start track is greater than end track.\n");
cleanup(20);
}
if (end >= src->numcyl) {
print3("Maximum end track on ", srcname, " is");
print2(numtostr(src->numcyl - 1), ".\n");
cleanup(20);
}
if ((newstart + (end - start)) >= dest->numcyl) {
print2("There is not room for ", numtostr(1 + end - start));
print3(" tracks on ", destname, "\n");
cleanup(20);
}
destport = (MSGPORT *)DeviceProc(destname);
if (!destport) {
print3("Can't locate process for device ", destname,"\n");
cleanup(20);
}
/*
* The two devices are valid, so now open the exec devices which
* they use.
*/
opendevs();
inhibit(destport, TRUE);
inhibited = TRUE;
copytracks(start, end, newstart);
cleanup(0);
}